home *** CD-ROM | disk | FTP | other *** search
- /* Copyright 1984 by the Massachusetts Institute of Technology */
-
- /*
- Copyright Cornell University 1986. All rights are reserved.
-
- As of 4/10/86:
- This source file may have no changes from the M.I.T original
- other than this notice; but it has been tested as part of
- Cornell's Aztec-C port. See notice.h
-
- */
-
-
- /* See permission and disclaimer notice in file "notice.h" */
- #include <notice.h>
-
- #include <em.h>
- #include <h19.h>
- #include "tftp.h"
-
- #ifndef DUALTCP
- /* declarations needed if MacTCP stands alone */
-
- int (*tfs_alert)();
- int (*tfs_done)();
-
- char macfile[256]; /* for Macintosh file name translation */
-
- int ntftps = 0;
-
-
- int tfstate = OFF;
- long refusedt = 0; /* time of most recent transfer refusal */
-
- static unsigned socket = 0;
-
- unsigned short udp_socket()
- {
- if (socket)
- return socket++;
-
- socket = cticks;
- if (socket < 1000)
- socket +=1000;
- return(socket++);
- }
-
-
- #endif
-
- extern unsigned NDEBUG;
-
- #define MINTICKS 60 /* 1 second */
-
- extern int (*tfs_alert)();
- extern int (*tfs_done)();
-
- extern char macfile[]; /* for Macintosh file name translation */
-
-
- extern int tfstate;
- extern long refusedt; /* time of most recent transfer refusal */
-
-
- /* Utility routines */
-
- /* Modified to eliminate dangerous resends, simplify round-trip
- time estimation, identify old and extra connections and shut
- them down, and use clock-tick timer, 12/83.
- Modified to use standard error code for old TID's and to byte-
- swap the error code. 12/23/83.
- Modified to check foreign port number before considering
- error packets to be meaningful. Also arranged to send
- error packets back in the incoming packet buffer, to
- avoid clobbering the current output packet, 1/84.
- Initialization of new connections improved to set file descriptor
- and udp connection pointers to null, to avoid errors on premature
- cleanup, 1/16/84. <J.H.Saltzer>
-
- 1/24/84 - added octet mode.
- <John Romkey>
- 1/2/85 - fixed length calculation bug in mtcp_tfudperr.
- <John Romkey>
- */
-
- extern char *malloc();
- unsigned long getmyA5();
-
- ip_addr p_host; /* source host of current packet */
- unsigned short p_port; /* source port of current packet */
-
- extern StreamPtr tfsstream;
- extern StreamPtr tftpstream;
- extern UDPiopb udpread;
-
- /* the TCP Async Notification Routine */
-
- pascal void udp_event(stream, eventCode, userDataPtr, icmpMsg)
- StreamPtr stream;
- short eventCode;
- Ptr userDataPtr;
- ICMPReport *icmpMsg;
- {
- unsigned long oldA5;
-
- oldA5 = getmyA5();
-
- switch (eventCode) {
- case UDPDataArrival: {
- #ifdef DATANOTIFY
- if (stream == tftpstream)
- tftpdata = TRUE;
- else if (stream == tfsstream)
- tfsdata = TRUE;
- #endif
- break;
- }
- case UDPICMPReceived: {
- /* prerr25("UDP ICMP Receive"); */
- break;
- }
- }
-
- setA5(oldA5);
-
- }
-
- /* called from tk_yield if udpdata has arrived */
-
- tftp_upcall()
- {
- int count = 0;
- OSErr errcode;
-
- if (tfsdata) {
- /* it's on the TFTP request socket */
- tfsdata = FALSE;
- p_host = tfspb.csParam.receive.remoteHost;
- p_port = tfspb.csParam.receive.remotePort;
- mtcp_tfshnd(tfspb.csParam.receive.rcvBuff,
- tfspb.csParam.receive.rcvBuffLen,
- tfspb.csParam.receive.remoteHost,
- tfspb.csParam.receive.remotePort,
- tfspb.csParam.receive.secondTimeStamp
- );
-
- /* now return the buffer to UDP */
- tfspb.csCode = UDPBfrReturn;
- if (errcode = PBControl(&tfspb, (Boolean) FALSE))
- error("no buffer return tfsread");
-
- /* and do another read */
- tfspb.csCode = UDPRead;
- tfspb.ioCompletion = tfs_read_done;
- tfspb.csParam.receive.timeOut = 0;
- if (errcode = PBControl(&tfspb, (Boolean) TRUE))
- error("no tfsread");
- tfs_read_req++;
- }
- if (tftpdata) {
- /* data on the TFTP transfer socket */
- tftpdata = FALSE;
- p_host = tftppb.csParam.receive.remoteHost;
- p_port = tftppb.csParam.receive.remotePort;
- mtcp_tftprcv(tftppb.csParam.receive.rcvBuff,
- tftppb.csParam.receive.rcvBuffLen,
- tftppb.csParam.receive.remoteHost,
- tftppb.csParam.receive.remotePort,
- &tftpconn
- );
- if (tftpconn.tf_state != DEAD) {
- /* if transfer died/finished don't do another read! */
-
- /* now return the buffer to UDP */
- tftppb.csCode = UDPBfrReturn;
- if (errcode = PBControl(&tftppb, (Boolean) FALSE))
- error("no buffer return tftpread");
-
- /* and do another read */
- tftppb.csCode = UDPRead;
- tftppb.ioCompletion = tftp_read_done;
- tftppb.csParam.receive.timeOut = 0;
- if (errcode = PBControl(&tftppb, (Boolean) TRUE)) {
- error("no tftpread");
- }
- tftp_read_req++;
- }
- }
- }
-
- /* write a tftp packet */
-
- mtcp_tf_write(cn, len)
- register struct tfconn *cn;
- unsigned len;
- {
- register struct tfack *packet;
- unsigned res;
-
- packet = cn->tf_outp;
- if (packet->tf_op == RRQ || packet->tf_op == WRQ) {
- cn->tf_tries = REQTRIES;
- }
- else
- cn->tf_tries = TFTPTRIES;
-
- cn->tf_lastlen = len;
- cn->tf_snt++;
- res = (macudp_write(cn, packet, len) == 0) ? -1 : 0;
-
- tm_tset((int) cn->tf_rt, mtcp_tftptmo, cn, cn->tf_tm);
- cn->tf_sent = cticks;
- cn->tf_NR = 1;
- }
-
-
- /* Dump a connection block for debugging purposes. */
-
- #ifdef TFTPDEBUG
-
- tfcndump(cn)
- register struct tfconn *cn;
- {
- printf("Connection addr = %04x\n", cn);
- printf("lastlen = %u\texpected = %u\n",
- cn->tf_lastlen, cn->tf_expected);
- printf("state = %u\tdir = %u\tmode = %u\n",
- cn->tf_state, cn->tf_dir, cn->tf_mode);
- printf("sent = %u\trcvd = %u\tous = %u\tmo = %u\trsnd = %u\n\n",
- cn->tf_snt, cn->tf_rcv, cn->tf_ous,cn->tf_ntmo, cn->tf_rsnd);
- printf("round trip delay = %U\tK = %u\tcurnt tmo = %U\n",
- cn->tf_trt, cn->tf_K, cn->tf_rt);
- printf("size = %U\n", cn->tf_size);
- }
- #endif
-
- #ifdef DOLOG
-
- /* Log a connection block.*/
-
- tfcnlog(cn)
- register struct tfconn *cn;
- {
- log(tftplog, "snt=%u rcvd=%u ous=%u tmo=%u rsnd=%u trt=%U",
- cn->tf_snt, cn->tf_rcv, cn->tf_ous, cn->tf_ntmo, cn->tf_rsnd, cn->tf_trt);
- }
-
- #endif
-
- /* patch the cn structure host/port to send a packet to a host w/ no connection */
-
- mtcp_tfrpyerr(cn, code, text)
- struct tfconn *cn;
- unsigned code;
- char *text;
- {
- ip_addr savehost;
- unsigned short saveport;
-
- savehost = cn->tf_host;
- saveport = cn->tf_port;
- cn->tf_host = p_host;
- cn->tf_port = p_port;
-
- mtcp_tfudperr(cn, code, text);
-
- cn->tf_host = savehost;
- cn->tf_port = saveport;
- }
-
-
- /* Send an error packet. If the code is nonzero, put it in the packet.
- Otherwise, copy the text into the packet. */
-
- char *errors[] = {
- "The JNC Memorial BUGHALT",
- "File not found",
- "Access violation",
- "Disk full",
- "Illegal TFTP operation",
- "Unknown transfer ID",
- "File already exists",
- "No such user"
- };
-
-
- mtcp_tfudperr(cn, code, text)
- struct tfconn *cn;
- unsigned code;
- char *text;
- {
- unsigned len;
- struct tferr perr;
- len = 4;
-
- perr.tf_op = ERROR;
- perr.tf_code = code;
-
- if (code == ERRTXT && text != NULL) {
- strncpy(&perr.tf_err[0], text, TFERRLEN);
- len += strlen(text) + 1;
- }
- else {
- strncpy(&perr.tf_err[0], errors[code], TFERRLEN);
- len += strlen(errors[code]) + 1;
- }
- #ifdef TFTPDEBUG
- if (NDEBUG & APTRACE)
- printf("TFTP: Sending error packet, code %u, <%s>\n",
- code, perr.tf_err);
- #endif
- return(macudp_write(cn, &perr, len));
- }
-
-
- /* Cleanup routine called when done */
-
- long mtcp_tfcleanup(cn)
- struct tfconn *cn;
- {
- long size;
-
- #ifdef TFTPDEBUG
- if (NDEBUG & INFOMSG)
- printf("TFCLEANUP called\n");
- #endif
- if (cn->tf_mode != TEST)
- fclose(cn->tf_fd);
- macudp_close(cn);
-
- tm_clear(cn->tf_tm);
- tm_free(cn->tf_tm);
- cn->tf_tm = NULL;
-
- free(cn->tf_outp);
- cn->tf_outp = NULL;
-
- cn->tf_state = DEAD;
- /* tfcndump(cn); */
- #ifdef DOLOG
- tfcnlog(cn);
- #endif
- size = cn->tf_size;
-
- return(size);
- }
-
-
- macudp_write(cn, packet, len)
- struct tfconn * cn;
- char * packet;
- int len;
- {
- UDPiopb wpb;
- OSErr errcode;
- struct trash {
- wdsEntry udp;
- int term;
- } wds;
-
- wds.udp.length = len;
- wds.udp.ptr = packet;
- wds.term = 0;
-
- wpb.ioCRefNum = ipp_refnum;
- wpb.csCode = UDPWrite;
- wpb.udpStream = cn->stream;
- wpb.csParam.send.remoteHost = cn->tf_host;
- wpb.csParam.send.remotePort = cn->tf_port;
- wpb.csParam.send.wdsPtr = &wds;
- wpb.csParam.send.checkSum = (Boolean) TRUE;
-
- if (errcode = PBControl(&wpb, (Boolean) FALSE)) {
- error("macudp_write failed");
- }
- }
-
-
- macudp_close(cn)
- struct tfconn *cn;
- {
- UDPiopb cpb;
-
- cpb.ioCRefNum = ipp_refnum;
- cpb.csCode = UDPRelease;
- cpb.udpStream = cn->stream;
- if (PBControl(&cpb, (Boolean) FALSE))
- error("MacTCP UDP release failed");
- }
-
-
- /* Handle an incoming ack */
-
- mtcp_tfdoack(cn, ack, len)
- register struct tfconn *cn;
- register struct tfack *ack;
- unsigned len;
- {
- if (ack->tf_block == cn->tf_expected) {
- tf_rtt(cn);
- tm_clear(cn->tf_tm);
-
- cn->tf_state = RCVACK;
- tfssend(cn);
- }
- else {
- /* We have received an ACK,
- but not for the data block we sent. It must be for
- a duplicate, since we wouldn't have sent
- the current data block if we hadn't gotten an ACK for
- the previous one. This duplicate ACK means either
- that the network resent a packet that it wasn't sure
- got through, or else the other end resent the ACK
- because our current data block is lost or late.
- In either case, we can safely ignore this extra ACK,
- and if the ACK we want doesn't come our own timer will
- get us started again. It isn't safe
- to resend the current data block now unless we are
- absolutely certain that the other end won't reack
- it if the earlier send was just delayed.
- */
-
- cn->tf_ous++;
- #ifdef TFTPDEBUG
- if (NDEBUG & APTRACE)
- printf("TFTP: ACK for block %u received again.\n",
- ack->tf_block);
- #endif
- }
- }
-
-
- /* ack a certain block number */
-
- mtcp_tfsndack(cn, number)
- register struct tfconn *cn;
- unsigned number;
- {
- register struct tfack *pack;
-
- pack = (struct tfack *) cn->tf_outp;
- cn->tf_lastlen = sizeof(struct tfack);
- pack->tf_op = ACK;
- pack->tf_block = number;
- #ifdef TFTPDEBUG
- if (NDEBUG & APTRACE)
- printf("TFTP: ACKing block %u\n", number);
- #endif
- return(mtcp_tf_write(cn, sizeof(struct tfack)));
- }
-
- /* timeout has occured, retry or kill? */
-
- mtcp_tftptmo(cn)
- register struct tfconn *cn;
- {
- #ifdef TFTPDEBUG
- if (NDEBUG & TMO)
- printf("TFTP: Timeout.\n");
- #endif
- cn->tf_ntmo++;
- if (--cn->tf_tries) {
- /* do a(nother) retry */
- #ifdef TFTPDEBUG
- if (NDEBUG & APTRACE)
- printf("TFTP: resending\n");
- #endif
- cn->tf_rsnd++;
- cn->tf_NR++;
- macudp_write(cn, cn->tf_outp, cn->tf_lastlen);
- tm_clear(cn->tf_tm);
- tm_tset((int) cn->tf_rt, mtcp_tftptmo, cn, cn->tf_tm);
- }
- else {
- cn->tf_state = TIMEOUT;
- if (cn->tf_dir == PUT)
- tfssend(cn);
- else
- tfsrcv(cn);
- }
- }
-
- mtcp_tfkill(cn)
- register struct tfconn *cn;
- {
- tm_clear(cn->tf_tm);
- cn->tf_state = DEAD;
- if (cn->tf_dir == PUT)
- tfsrcv(cn);
- else
- tfssend(cn);
- }
-
-
-
-
- /* Process a data packet received for TFTP connection cn, according to the
- type (ASCII, IMAGE, TEST) specified in the connection block. Also
- handle out of secquence blocks and check on block length. If a block
- is way to short (len < tftp header size) send back an error message
- and abort the transfer; we have CSR disease. If the block is less
- than 512 bytes long, shut down the transfer; we're done.
- Otherwise, just write it to disk (if necessary).
- */
-
- mtcp_tfdodata(cn, ptfdat, len)
- register struct tfconn *cn;
- struct tfdata *ptfdat;
- unsigned len;
- {
- register char *data;
- unsigned count;
- static int crseen = FALSE;
- extern char macfile[];
-
- if (len < 4) {
- mtcp_tfudperr(cn, ERRTXT, "You have CSR disease.");
- /* rpy */
- mtcp_tfkill(cn);
- return(0);
- }
-
- len -= 4; /* BAD. */
-
- if (ptfdat->tf_block != cn->tf_expected) {
- /* We got a retransmission of a packet we have already tried to
- ACK. If we retransmit the ACK, and the old ACK finally gets through also,
- our correspondent will resend the next data block, and we will do the same
- thing for it, on through to the end of the file. So we shouldn't retransmit
- the ACK until we are convinced that the first one was actually lost. We will
- become convinced if our own timeout waiting for the next data packet
- expires.
- Here is what you shouldn't do. . .
- if (ptfdat->tf_block == cn->tf_expected-1)
- mtcp_tfsndack(cn, ptfdat->tf_block);
- And now we return to correct procedures. . .
- */
- #ifdef TFTPDEBUG
- if (NDEBUG & (INFOMSG|PROTERR|APTRACE))
- printf("TFTP: Got block %u, expecting %u.\n",
- ptfdat->tf_block, cn->tf_expected);
- #endif
- cn->tf_ous++;
- return(0);
- }
-
- if (cn->tf_state != DATAWAIT) {
- #ifdef TFTPDEBUG
- printf("TFTP: Received unexpected data block tf_state is %d.\n",cn->tf_state);
- #endif
- mtcp_tfudperr(cn, ERRTXT, "Rcvd unexpected data block");
- /* mtcp_tfrpyerr */
- mtcp_tfkill(cn);
- return(0);
- }
-
- tm_clear(cn->tf_tm);
- tf_rtt(cn);
-
- cn->tf_size += len;
- data = ptfdat->tf_data;
-
- if (cn->tf_mode == IMAGE || cn->tf_mode == TEST || cn->tf_mode == OCTET) {
- if (cn->tf_mode != TEST && fwrite(data, 1, len, cn->tf_fd) != len)
- {
- mtcp_tfudperr(cn, DISKFULL, &macfile[0]);
- /* printf("TFTP: disk is full!\n"); */
- mtcp_tfkill(cn);
- return(0);
- }
- }
- else if (cn->tf_mode == ASCII) {
- register char thechar;
-
- for (count = len + 1; --count; ) {
- if ( (thechar = *data++) ) {
- if (thechar == LF) {
- if (crseen) {
- crseen = FALSE;
- continue;
- }
- else
- thechar = CR;
- }
- else if (thechar == CR) {
- crseen = TRUE;
- }
- if (putc(thechar, cn->tf_fd) == EOF) {
- mtcp_tfudperr(cn, DISKFULL, &macfile[0]);
- /* printf("TFTP: disk is full!\n"); */
- mtcp_tfkill(cn);
- return(0);
- }
- }
- }
- }
- else {
- mtcp_tfudperr(cn, ERRTXT, "Internal error");
- mtcp_tfkill(cn);
- return(0);
- }
-
- if (len == NORMLEN)
- cn->tf_state = RCVDATA;
- else
- cn->tf_state = RCVLASTDATA;
-
- /* Send the ack AFTER writing the data */
- mtcp_tfsndack(cn, ptfdat->tf_block);
-
- cn->tf_expected++;
-
- tfsrcv(cn);
- }
-
- /* recalculate round trip time and set vars */
-
- tf_rtt(cn)
- register struct tfconn *cn;
- {
- long trtM;
-
- trtM = cticks - cn->tf_sent; /* Measured round trip time */
- if (cn->tf_NR_last == 1)
- cn->tf_K = Kinit;
-
- if (cn->tf_NR == 1)
- cn->tf_trt = (trtM + cn->tf_trt) / 2;
- else {
- if ((cn->tf_NR_last > 1) && (cn->tf_K > 1) )
- cn->tf_K -= Kinc;
- cn->tf_trt += cn->tf_trt / cn->tf_K;
- }
- cn->tf_rt = max(min(cn->tf_trt * TMMULT, MAXTMO), MINTICKS);
- cn->tf_NR_last = cn->tf_NR;
- }
-
-
-
- /* Handle an incoming packet after a connection has been opened */
-
- mtcp_tftprcv(pdata, len, fhost, port, cn)
- struct tfdata *pdata;
- unsigned len;
- ip_addr fhost;
- unsigned short port;
- struct tfconn *cn;
- {
- unsigned op;
-
- cn->tf_rcv++;
-
- op = pdata->tf_op;
- pdata->tf_op = op;
-
- switch (op) {
- case DATA: {
- if (mtcp_tfckport(cn, pdata, port))
- mtcp_tfdodata(cn, pdata, len);
- break;
- }
- case ACK: {
- if (mtcp_tfckport(cn, pdata, port))
- mtcp_tfdoack(cn, pdata, len);
- break;
- }
- case ERROR: {
- if ((cn->tf_haveport == 0) ||
- (cn->tf_port == port)) {
- mtcp_tfdoerr(cn, pdata, len);
- mtcp_tfkill(cn);
- }
- #ifdef TFTPDEBUG
- else if (NDEBUG & ROUTE) {
- printf("TFTP: ignoring error packet.\n");
- mtcp_tfdoerr(cn, pdata, len);
- }
- #endif
- break;
- }
- default: {
- error("mtcp_tftprcv: Got bad opcode %u", op);
- mtcp_tfudperr(cn, ILLTFTP, " ");
- mtcp_tfkill(cn);
- break;
- }
- }
- return(0);
- }
-
-
- mtcp_tfsndata(cn, len)
- register struct tfconn *cn;
- unsigned len;
- {
- register struct tfdata *tfdata;
-
- tfdata = cn->tf_outp;
- tfdata->tf_op = DATA;
- tfdata->tf_block = cn->tf_expected;
-
- #ifdef DEBUG
- if (NDEBUG & APTRACE)
- printf("TFTP: sending block %u\n", tfdata->tf_block);
- #endif
- return(mtcp_tf_write(cn, sizeof(struct tfdata) - 512 + len));
- }
-
- /* Setup a TFTP connection block. */
-
- mttfmkcn(cn, dir, mode)
- register struct tfconn * cn;
- unsigned dir;
- unsigned mode;
- {
- cn->tf_fd = NULL;
- cn->tf_udp = NULL;
- cn->tf_rcv = NULL;
- cn->tf_snt = 0;
- cn->tf_ous = 0;
- cn->tf_ntmo = 0;
- cn->tf_rsnd = 0;
- cn->tf_dir = dir;
- cn->tf_mode = mode;
- cn->tf_size = 0L;
- cn->tf_K = Kinit;
- cn->tf_trt = T0;
- cn->tf_rt = (long) min(cn->tf_trt * TMMULT, MAXTMO);
- cn->tf_NR = 0;
- cn->tf_NR_last = 1;
-
-
- if (cn->tf_tm == NULL) {
- cn->tf_tm = tm_alloc();
- if (cn->tf_tm == NULL) {
- error("TFTP: Couldn't allocate timer");
- return(-1);
- }
- }
- if (cn->tf_outp == NULL) {
- cn->tf_outp = malloc(NORMLEN);
- if (cn->tf_outp == NULL) {
- error("TFTP: Couldn't allocate output packet");
- tm_free(cn->tf_tm);
- cn->tf_tm = NULL;
- return(-2);
- }
- }
- return(0);
- }
-
-
- /* Check over the port in the incoming packet */
-
- mtcp_tfckport(cn, pdata, pfport)
- register struct tfconn *cn;
- register struct tfdata *pdata;
- unsigned short pfport;
- {
- if (cn->tf_haveport == 0) {
- /* Foreign port not yet identified, save it. */
- if (cn->tf_expected == pdata->tf_block) {
- /* but only if this is a response to our request. */
- cn->tf_haveport = TRUE;
- cn->tf_port = pfport;
- }
- else {
- #ifdef TFTPDEBUG
- if (NDEBUG & (PROTERR|NETERR))
- printf("TFTP: Received packet from old connection.\n");
- printf(" Expected block %u, got block %u\n",
- cn->tf_expected, pdata->tf_block);
- #endif
- mtcp_tfrpyerr(cn, ERRTXT, "Rcvd data on old connection");
- /* rpy */
- return(FALSE);
- }
- }
- else if (cn->tf_port != pfport) {
- #ifdef TFTPDEBUG
- if (NDEBUG & (PROTERR|NETERR)) {
- printf("TFTP: Rcvd pkt from port %04x, expect port %04x,\n",
- pfport, cn->tf_port);
- }
- #endif
- mtcp_tfrpyerr(cn, BADTID, " ");
- /* rpy */
- return(FALSE);
- }
- return(TRUE);
- }
-
-
- /* Process an incoming error packet */
-
- mtcp_tfdoerr(cn, perr, len)
- register struct tfconn *cn;
- struct tferr * perr;
- unsigned len;
- {
- char terror[100];
-
- sprintf(terror, "TFTP: Error from host: \"%s\"", &perr->tf_err[0]);
- error(terror);
- }
-
-